#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <cstring>

const int MAX_EVENTS = 1024;
const int PORT = 8888;

// 设置非阻塞模式‌:ml-citation{ref="2,7" data="citationList"}
void setNonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    // 1. 创建监听Socket‌:ml-citation{ref="1,4" data="citationList"}
    int listenFd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
    if(listenFd == -1) {
        perror("socket create failed");
        exit(EXIT_FAILURE);
    }

    setNonblocking(listenFd);

    // 地址复用配置‌:ml-citation{ref="2" data="citationList"}
    int opt = 1;
    setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定地址‌:ml-citation{ref="4" data="citationList"}
    sockaddr_in servAddr{AF_INET, htons(PORT), INADDR_ANY};
    if(bind(listenFd, (sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
        perror("bind failed");
        close(listenFd);
        exit(EXIT_FAILURE);
    }

    // 开始监听‌:ml-citation{ref="1" data="citationList"}
    if(listen(listenFd, SOMAXCONN) == -1) {
        perror("listen failed");
        close(listenFd);
        exit(EXIT_FAILURE);
    }

    // 2. 创建epoll实例‌:ml-citation{ref="2,6" data="citationList"}
    int epollFd = epoll_create1(0);
    epoll_event ev{.events = EPOLLIN | EPOLLET, .data = {.fd = listenFd}};
    epoll_ctl(epollFd, EPOLL_CTL_ADD, listenFd, &ev);

    // 3. 事件循环‌:ml-citation{ref="6,7" data="citationList"}
    epoll_event events[MAX_EVENTS];
    while(true) {
        int readyNum = epoll_wait(epollFd, events, MAX_EVENTS, -1);
        std::cout << "client ready num : " << readyNum << std::endl;
        if(readyNum == -1) {
            if(errno == EINTR) continue;
            perror("epoll_wait error");
            break;
        }

        for(int i=0; i<readyNum; ++i) {
            // 处理新连接‌:ml-citation{ref="1,7" data="citationList"}
            if(events[i].data.fd == listenFd) {
                sockaddr_in cliAddr{};
                socklen_t len = sizeof(cliAddr);
                int connFd = accept4(listenFd, (sockaddr*)&cliAddr, &len, SOCK_NONBLOCK);
                if(connFd == -1) {
                    perror("accept error");
                    continue;
                }

                // 注册客户端socket‌:ml-citation{ref="2" data="citationList"}
                epoll_event clientEv{.events = EPOLLIN | EPOLLRDHUP | EPOLLET, .data = {.fd = connFd}};
                epoll_ctl(epollFd, EPOLL_CTL_ADD, connFd, &clientEv);

                std::cout << "New client connected: " << inet_ntoa(cliAddr.sin_addr) 
                          << ":" << ntohs(cliAddr.sin_port) << std::endl;
            } 
            // 处理客户端事件‌:ml-citation{ref="6,7" data="citationList"}
            else {
                int clientFd = events[i].data.fd;
                // 连接关闭检测‌:ml-citation{ref="2" data="citationList"}
                if(events[i].events & (EPOLLRDHUP | EPOLLHUP)) {
                    epoll_ctl(epollFd, EPOLL_CTL_DEL, clientFd, nullptr);
                    close(clientFd);
                    continue;
                }

                // 数据读取处理‌:ml-citation{ref="6" data="citationList"}
                if(events[i].events & EPOLLIN) {
                    char buffer[4096];
                    ssize_t n;
                    // ET模式循环读取‌:ml-citation{ref="7" data="citationList"}
                    while((n = recv(clientFd, buffer, sizeof(buffer), 0)) > 0) {
                        send(clientFd, buffer, n, 0); // 回显业务逻辑
                        buffer[n] = '\0';
                        std::cout << "send: " << buffer << std::endl;
                    }

                    // 连接关闭或错误‌:ml-citation{ref="2" data="citationList"}
                    if(n == 0 || (n == -1 && errno != EAGAIN)) {
                        epoll_ctl(epollFd, EPOLL_CTL_DEL, clientFd, nullptr);
                        close(clientFd);
                    }
                }
            }
        }
    }

    // 资源清理‌:ml-citation{ref="2" data="citationList"}
    close(listenFd);
    close(epollFd);
    return 0;
}
